//
// Created by long_ on 2021/9/14.
//

#ifndef INTERFACE_PLUGIN_BUFFER_H
#define INTERFACE_PLUGIN_BUFFER_H

#include <memory>
#include <map>

#include "plugin_tags.h"
#include "plugin_types.h"
#include "plugin_audio_tags.h"
#include "plugin_video_tags.h"

namespace OHOS {
namespace Multimedia {
namespace Plugin {

#define BUFFER_FLAG_EOS 0x00000001
struct Allocator {
    virtual ~Allocator() = default;
    virtual void* Alloc(size_t size) = 0;
    virtual void Free(void* ptr) = 0;
};

enum struct BufferMetaType : uint32_t {
    AUDIO,
    VIDEO,
};

class Memory {
public:
    ~Memory() = default;
    size_t GetCapacity();
    size_t GetSize();
    const uint8_t* GetReadOnlyData(size_t position = 0);
    uint8_t *GetWritableData(size_t size, size_t position = 0);
    size_t Write(const uint8_t* in, size_t size, size_t position = std::string::npos);
    size_t Read(uint8_t* out, size_t size, size_t position = std::string::npos);
    void Reset();

private:
    Memory(size_t capacity, std::shared_ptr<uint8_t> bufData, size_t align = 0);
    Memory(size_t capacity, std::shared_ptr<Allocator> allocator = nullptr, size_t align = 0);

private:
    size_t capacity;
    size_t alignment;
    size_t size;
    std::shared_ptr<Allocator> allocator;
    std::shared_ptr<uint8_t> addr;
    friend class Buffer;
};

class BufferMeta {
public:
    ~BufferMeta() = default;
    ValueType GetMeta(Tag tag);
    void SetMeta(Tag tag, ValueType value);
    BufferMetaType GetType();

protected:
    explicit BufferMeta(BufferMetaType type);

private:
    BufferMetaType type;
    std::shared_ptr<TagMap> tags;
};

class AudioBufferMeta : public BufferMeta
{
public:
    ~AudioBufferMeta() = default;
    size_t samples;
    AudioSampleFormat sampleFormat;
    uint32_t sampleRate;
    uint32_t channels;
    uint32_t bytesPreFrame;
    AudioChannelLayout channelLayout;
    std::vector<size_t> offsets;
private:
    AudioBufferMeta() : BufferMeta(BufferMetaType::AUDIO) {}
    friend class Buffer;
};

class VideoBufferMeta : public BufferMeta {
public:
    ~VideoBufferMeta() = default;
    VideoPixelFormat videoPixelFormat;
    uint32_t id;
    uint32_t width;
    uint32_t height;
    uint32_t planes;
    std::vector<uint32_t> stride;
    std::vector<uint32_t> offset;
private:
    VideoBufferMeta() : BufferMeta(BufferMetaType::VIDEO) {}
    friend class Buffer;
};

class Buffer {
public:
    explicit Buffer(BufferMetaType type = BufferMetaType::AUDIO);
    ~Buffer() = default;
    static std::shared_ptr<Buffer> CreateDefaultBuffer(BufferMetaType type, size_t capacity,
                                                       std::shared_ptr<Allocator> allocator = nullptr,
                                                       size_t align = 0);
    std::shared_ptr<Memory> WrapMemory(uint8_t* data, size capacity, size_t size);
    std::shared_ptr<Memory> WrapMemoryPtr(std::shared_ptr<uint8_t> data, size capacity, size_t size);
    std::shared_ptr<Memory> AllocMemory(std::shared_ptr<Allocator> allocator, size capacity, size_t align=0);
    uint32_t GetMemoryCount();
    std::shared_ptr<Memory> GetMemory(uint32_t index = 0);
    std::shared_ptr<BufferMeta> GetBufferMeta();
    void Reset();
    bool IsEmpty();
    uint32_t streamID;
    uint64_t pts;
    uint64_t dts;
    uint64_t duration;
    uint64_t flag;
private:
    std::vector<std::shared_ptr<Memory>> data;
    std::shared_ptr<BufferMeta> meta;
};


}
}
}

#endif //INTERFACE_PLUGIN_BUFFER_H
